package pl.edu.agh.gronoMining.owlToGraphConverter.parser.implementation;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.text.ParseException;
import java.util.Collection;

import org.apache.log4j.Logger;

import pl.edu.agh.gronoMining.owlToGraphConverter.graph.Graph;
import pl.edu.agh.gronoMining.owlToGraphConverter.parser.IOwlParser;
import pl.edu.agh.gronoMining.owlToGraphConverter.parser.utils.IConverter;
import pl.edu.agh.gronoMining.owlToGraphConverter.parser.utils.IDataManager;
import pl.edu.agh.gronoMining.owlToGraphConverter.parser.utils.NodeData;
import pl.edu.agh.gronoMining.owlToGraphConverter.parser.utils.implementation.NodesManager;
import edu.stanford.smi.protegex.owl.ProtegeOWL;
import edu.stanford.smi.protegex.owl.model.OWLIndividual;
import edu.stanford.smi.protegex.owl.model.OWLModel;
import edu.stanford.smi.protegex.owl.model.OWLNamedClass;
import edu.stanford.smi.protegex.owl.model.OWLObjectProperty;
import edu.stanford.smi.protegex.owl.model.OWLProperty;

public class OwlParser1 implements IOwlParser {

	private IDataManager dataManager;
	private IConverter converter;
	
	private NodesManager<String> usersManager;
	private NodesManager<String> gronoNameManager;
	private NodesManager<String> gronoSubjectManager;
	private NodesManager<String> cityManager;
	private NodesManager<String> provinceManager;
	private NodesManager<String> countryManager;
	
	private static Logger logger = Logger.getLogger(OwlParser1.class);
	
	public OwlParser1 (IDataManager dataManager, IConverter converter) {
		
		this.dataManager = dataManager;
		this.converter = converter;
		
		this.usersManager = new NodesManager<String>();
		this.gronoNameManager = new NodesManager<String>();
		this.gronoSubjectManager = new NodesManager<String>();
		this.cityManager = new NodesManager<String>();
		this.provinceManager = new NodesManager<String>();
		this.countryManager = new NodesManager<String>();
		
		logger.info("Parser inicialized");
	}
	
	public void prepare() {
		
	}
	
	public Graph[] parse(InputStream in) throws IOException, ParseException {
		
		logger.info("Parser started");
		Graph graph = new Graph();
		try {
			
			BufferedReader bin = new BufferedReader(new InputStreamReader(in));
			OWLModel owlModel = ProtegeOWL.createJenaOWLModelFromReader(bin);
			
			logger.info("model created");
			
			OWLNamedClass classUser = owlModel.getOWLNamedClass("User");
			OWLNamedClass classGrono = owlModel.getOWLNamedClass("Grono");
			OWLNamedClass classCity = owlModel.getOWLNamedClass("Miasto");
			OWLNamedClass classProvince = owlModel.getOWLNamedClass("Wojewodztwo");
			OWLNamedClass classCountry = owlModel.getOWLNamedClass("Kraj");
			
			OWLProperty propertyMaPlec = owlModel.getOWLProperty("maPlec");
			OWLProperty propertyZarejestrowalSie = owlModel.getOWLProperty("zarejestrowalSie");
			OWLProperty propertyMaImie = owlModel.getOWLProperty("maImie");
			OWLProperty propertyOstatnioLogowalSie = owlModel.getOWLProperty("ostatnioLogowalSie");
			OWLProperty propertySzuka = owlModel.getOWLProperty("szuka");
			OWLProperty propertyMaNazwe = owlModel.getOWLProperty("maNazwe");
			OWLProperty propertyZawieraSieWKategorii = owlModel.getOWLProperty("zawieraSieWKategorii");
			OWLProperty propertyUrodzilSie = owlModel.getOWLProperty("urodzilSie");
			
			OWLProperty propertyPiszeNaNim = owlModel.getOWLProperty("piszeNaNim");
			OWLObjectProperty objPropertyZawieraSieW = owlModel.getOWLObjectProperty("zawieraSieW");
			OWLObjectProperty objPropertyGraniczyZ = owlModel.getOWLObjectProperty("graniczyZ");
			OWLProperty propertyPiszeNa = owlModel.getOWLProperty("piszeNa");
			OWLProperty propertyPochodziZNiego = owlModel.getOWLProperty("pochodziZNiego");
			OWLProperty propertyMaZnajomego = owlModel.getOWLProperty("maZnajomego");
			OWLProperty propertyPochodziZ = owlModel.getOWLProperty("pochodziZ");
			OWLObjectProperty objPropertyZawiera = owlModel.getOWLObjectProperty("zawiera");
			OWLProperty propertyZaproszonyPrzez = owlModel.getOWLProperty("zaproszonyPrzez");
			
			logger.info("classes and properties created");
			
			Collection instancesOfCountry = classCountry.getInstances(false);
			for (Object countryObj : instancesOfCountry) {
				 
				OWLIndividual countryIndividual = (OWLIndividual) countryObj;
				
				String name = countryIndividual.getName();

				NodeData nodeData = new NodeData(NodeData.KRAJ, name);
				int nodeId = graph.addNode(NodeData.KRAJ,dataManager.getInt(nodeData),name);
				countryManager.setNode(name, nodeId);
				logger.info(" - add node - [" + nodeId + "]" + name);
			}
			for (Object countryObj : instancesOfCountry) {
				
				OWLIndividual countryIndividual = (OWLIndividual) countryObj;
				String name = countryIndividual.getName();
				int nodeId = countryManager.getNode(name);
				if (countryIndividual.hasPropertyValue(objPropertyGraniczyZ)) {
					
					Collection neighbours = countryIndividual.getPropertyValues(objPropertyGraniczyZ);
					for (Object neighbourObj : neighbours) {
						OWLIndividual neighbourIndividual = (OWLIndividual) neighbourObj;
						String neighbourName = neighbourIndividual.getName();
						if (neighbourIndividual.getRDFType().equals(classCountry)) {
							int neighbourId = countryManager.getNode(neighbourName);
							if (graph.hasEdge(neighbourId, nodeId) && nodeId != neighbourId) {
								graph.addEdge(neighbourId, nodeId);
								logger.info(" - add edge - ["+nodeId+"]"+name + " - [" + neighbourId+"]"+neighbourName);
							}
						}
					}
				}
			}
			 
			logger.info("countries parsed");
			
			Collection instancesOfProvince = classProvince.getInstances(false);
			for (Object provinceObj : instancesOfProvince) {
				OWLIndividual provinceIndividual = (OWLIndividual) provinceObj;
				String name = provinceIndividual.getName();

				NodeData nodeData = new NodeData(NodeData.WOJEWODZTWO, name);
				int nodeId = graph.addNode(NodeData.WOJEWODZTWO,dataManager.getInt(nodeData),name);
				provinceManager.setNode(name, nodeId);
				
				logger.info(" - add node - [" + nodeId + "]" + name);
				
				if (provinceIndividual.hasPropertyValue(objPropertyZawieraSieW)) {
					OWLIndividual countryIndividual = (OWLIndividual)provinceIndividual.getPropertyValue(objPropertyZawieraSieW);
					String countryName = countryIndividual.getName();
					int countryNodeId = countryManager.getNode(countryName);
					
					int tempNodeId = graph.addNode(NodeData.ZAWIERA_SIE,dataManager.getInt(new NodeData(NodeData.ZAWIERA_SIE, null)),"zawieraSie");
					graph.addEdge(nodeId, tempNodeId);
					graph.addEdge(countryNodeId, tempNodeId);
					//graph.addEdge(countryNodeId, nodeId);
					logger.info(" - add edge - ["+nodeId+"]"+name + " - [" + countryNodeId+"]"+countryName);
				}
			}
			
			for (Object provinceObj : instancesOfProvince) {
				
				OWLIndividual provinceIndividual = (OWLIndividual) provinceObj;
				String name = provinceIndividual.getName();
				int nodeId = provinceManager.getNode(name);
				if (provinceIndividual.hasPropertyValue(objPropertyGraniczyZ)) {
					
					Collection neighbours = provinceIndividual.getPropertyValues(objPropertyGraniczyZ);
					for (Object neighbourObj : neighbours) {
						OWLIndividual neighbourIndividual = (OWLIndividual) neighbourObj;
						String neighbourName = neighbourIndividual.getName();
						Integer neighbourId = null;
						if (neighbourIndividual.getRDFType().equals(classCountry)) {
							neighbourId = countryManager.getNode(neighbourName);
						} else if (neighbourIndividual.getRDFType().equals(classProvince)) {
							neighbourId = provinceManager.getNode(neighbourName);
						}
						if (neighbourId != null) {
							if (graph.hasEdge(neighbourId, nodeId)) {
								graph.addEdge(neighbourId, nodeId);
								logger.info(" - add edge - ["+nodeId+"]"+name + " - ["+neighbourId+"]" + neighbourName);
							}
						}
					}
				}
			}
			
			logger.info("provinces parsed");
			
			Collection instancesOfCities = classCity.getInstances(false);
			for (Object cityObj : instancesOfCities) {
				OWLIndividual cityIndividual = (OWLIndividual) cityObj;
				String name = cityIndividual.getName();

				NodeData nodeData = new NodeData(NodeData.MIASTO, name);
				int nodeId = graph.addNode(NodeData.MIASTO,dataManager.getInt(nodeData),name);
				cityManager.setNode(name, nodeId);
				
				logger.info(" - add node - [" + nodeId + "]" + name);
				if (cityIndividual.hasPropertyValue(objPropertyZawieraSieW)) {
					OWLIndividual provinceIndividual = (OWLIndividual)cityIndividual.getPropertyValue(objPropertyZawieraSieW);
					String provinceName = provinceIndividual.getName();
					int provinceNodeId = provinceManager.getNode(provinceName);
					graph.addEdge(provinceNodeId, nodeId);
					logger.info(" - add edge ["+nodeId+"]" + name + " - ["+provinceNodeId+"]" + provinceName);
				}
			}
			
			logger.info("cities parsed");
			
			Collection instancesOfGrono = classGrono.getInstances(false);
			for (Object gronoObj : instancesOfGrono) {
				OWLIndividual gronoIndividual = (OWLIndividual) gronoObj;
				
				String id = gronoIndividual.getName();
				String name = (String) gronoIndividual.getPropertyValue(propertyMaNazwe);
				String category = (String) gronoIndividual.getPropertyValue(propertyZawieraSieWKategorii);
				
				NodeData nodeData = new NodeData(NodeData.NAZWA_GRONA, name);
				int nodeId = graph.addNode(NodeData.NAZWA_GRONA,dataManager.getInt(nodeData), name);
				gronoNameManager.setNode(name, nodeId);
				logger.info("add node for grono - [" + nodeId + "]'"+name+"'");
				
				Integer subjectNodeId = gronoSubjectManager.getNode(category);
				if (subjectNodeId == null) {
					NodeData subjectNodeData = new NodeData(NodeData.TEMAT_GRONA, category);
					subjectNodeId = graph.addNode(NodeData.TEMAT_GRONA,dataManager.getInt(nodeData),category);
					gronoSubjectManager.setNode(category, subjectNodeId);
					logger.info("add node for grono subject -[" + subjectNodeId + "]'"+category+"'");
				}
				graph.addEdge(nodeId, subjectNodeId);
				logger.info(" - add edge ["+nodeId+"]" + name + " - ["+subjectNodeId+"]" + category);
			}
			
			logger.info("grono parsed");
			
			Collection instancesOfUser = classUser.getInstances(false);
			for (Object userObj : instancesOfUser) {
				OWLIndividual userIndividual = (OWLIndividual) userObj;
				
				String id = userIndividual.getName();
				logger.info(" === parsing user "+id);
				
				Integer userNodeId = usersManager.getNode(id);
				if (userNodeId == null) {
					userNodeId = graph.addNode(NodeData.USER,dataManager.getInt(new NodeData(NodeData.USER, null)), id);
					usersManager.setNode(id, userNodeId);
					logger.info("add node - [" + userNodeId + "] "+id);
				} else {
					logger.info("user node exists ("+userNodeId+")");
				}
				
				/*if (userIndividual.hasPropertyValue(propertyMaImie)) {
					String name = (String) userIndividual.getPropertyValue(propertyMaImie);
					name = name.trim();
					if (! "".equals(name)) {
						NodeData nameNodeData = new NodeData(NodeData.NodeTypes.imie, name);
						int nameNodeId = graph.addNode(dataManager.getInt(nameNodeData), name);
						graph.addEdge(userNodeId, nameNodeId);
						logger.log("name added ["+nameNodeId+"] - "+name);
					}
				}
				
				if (userIndividual.hasPropertyValue(propertyMaPlec)) {
					try {
						String sex = (String) userIndividual.getPropertyValue(propertyMaPlec);
						int sexInt = converter.sex(sex);
						NodeData sexNodeData = new NodeData(NodeData.NodeTypes.plec, sexInt);
						int sexNodeId = graph.addNode(dataManager.getInt(sexNodeData), "plec-"+sexInt);
						graph.addEdge(userNodeId, sexNodeId);
						logger.log("sex added ["+sexNodeId+"] - "+sexInt);
					} catch (ParseException e) {
						
					}
				}
				
				if (userIndividual.hasPropertyValue(propertyZarejestrowalSie)) {
					try {
						String registrationDate = (String) userIndividual.getPropertyValue(propertyZarejestrowalSie);
						int registrationInterval = converter.registryDate(registrationDate);
						NodeData registrationNodeData = new NodeData(NodeData.NodeTypes.dataRejestracji, registrationInterval);
						int registrationNodeId = graph.addNode(dataManager.getInt(registrationNodeData),"rejestracja-"+registrationInterval);
						graph.addEdge(userNodeId, registrationNodeId);
						logger.log("registration date added ["+registrationNodeId+"] - "+registrationInterval);
					} catch (ParseException e) {
						
					}
				}
				
				if (userIndividual.hasPropertyValue(propertyUrodzilSie)) {
					try {
						String birthDate = (String) userIndividual.getPropertyValue(propertyUrodzilSie);
						int ageInterval = converter.age(birthDate);
						NodeData ageNodeData = new NodeData(NodeData.NodeTypes.wiek, ageInterval);
						int ageNodeId = graph.addNode(dataManager.getInt(ageNodeData),"wiek-"+ageInterval);
						graph.addEdge(userNodeId, ageNodeId);
						logger.log("age added ["+ageNodeId+"] - " + ageInterval);
					} catch (ParseException e) {
						
					}
				}
				
				if (userIndividual.hasPropertyValue(propertyOstatnioLogowalSie)) {
					try {
						String lastLogDate = (String) userIndividual.getPropertyValue(propertyOstatnioLogowalSie);
						int lastLogDateInterval = converter.lastLogTime(lastLogDate);
						NodeData lastLogNodeData = new NodeData(NodeData.NodeTypes.czasLogowania, lastLogDateInterval);
						int lastLogNodeId = graph.addNode(dataManager.getInt(lastLogNodeData),"ostatniLog-"+lastLogDateInterval);
						graph.addEdge(userNodeId, lastLogNodeId);
						logger.log("last log added ["+lastLogNodeId+"] - "+lastLogDateInterval);
					} catch (ParseException e) {
						
					}
				}
				
				if (userIndividual.hasPropertyValue(propertySzuka)) {
					Collection lookForObjects = userIndividual.getPropertyValues(propertySzuka);
					for (Object lookForObj : lookForObjects) {
						String lookForName = (String) lookForObj;
						try {
							int interval = converter.lookFor(lookForName);
							NodeData lookForNodeData = new NodeData(NodeData.NodeTypes.szuka, interval);
							int lookForNodeId = graph.addNode(dataManager.getInt(lookForNodeData),"szuka-"+interval);
							graph.addEdge(userNodeId, lookForNodeId);
							logger.log("look for added ["+lookForNodeId+"] - " + interval);
						} catch (ParseException e) {
							
						}
					}
				}*/
				
				if (userIndividual.hasPropertyValue(propertyZaproszonyPrzez)) {
					String invitedById = (String) userIndividual.getPropertyValue(propertyZaproszonyPrzez);
					Integer invitedByNodeId = usersManager.getNode(invitedById);
					if (invitedByNodeId == null) {
						invitedByNodeId = graph.addNode(NodeData.USER,dataManager.getInt(new NodeData(NodeData.USER, null)),invitedById);
						usersManager.setNode(invitedById, invitedByNodeId);
						logger.info("add node - [" + invitedByNodeId + "] - "+invitedById);
					}
					int tempNodeId = graph.addNode(NodeData.ZAPROSZONY_PRZEZ,dataManager.getInt(new NodeData(NodeData.ZAPROSZONY_PRZEZ, null)),"zaproszonyPrzez");
					graph.addEdge(userNodeId, tempNodeId);
					graph.addEdge(invitedByNodeId, tempNodeId);
					//graph.addEdge(countryNodeId, nodeId);
					logger.info(" - add edge - ["+userNodeId+"]"+id + " - [" + invitedByNodeId+"]"+invitedById);
				}
				
				if (userIndividual.hasPropertyValue(propertyMaZnajomego)) {
					Collection friendsCollection = userIndividual.getPropertyValues(propertyMaZnajomego);
					for (Object friendObj : friendsCollection) {
						String friendId = (String) friendObj;
						friendId = friendId.trim();
						Integer friendNodeId = usersManager.getNode(friendId);
						if (friendNodeId == null) {
							//friendNodeId = graph.addNode(dataManager.getInt(new NodeData(NodeData.NodeTypes.user, null)),friendId);
							//usersManager.setNode(friendId, friendNodeId);
							//logger.log("add node - [" + friendNodeId + "] - "+friendId);
							continue;
						}
						
						if (friendNodeId != null) {
							if (! graph.hasEdge(userNodeId, friendNodeId)) {
								graph.addEdge(userNodeId, friendNodeId);
								logger.info(" - add edge ["+userNodeId+"]" + id + " - ["+friendNodeId+"]" + friendId);
							}
						}
						
					}
				}
				
				if (userIndividual.hasPropertyValue(propertyPiszeNa)) {
					Collection gronoCollection = userIndividual.getPropertyValues(propertyPiszeNa);
					for (Object gronoObj : gronoCollection) {
						String gronoName = (String) gronoObj;
						gronoName = gronoName.trim();
						Integer gronoNodeId = gronoNameManager.getNode(gronoName);
						if (gronoNodeId == null) {
							gronoNodeId = graph.addNode(NodeData.NAZWA_GRONA,dataManager.getInt(new NodeData(NodeData.NAZWA_GRONA, gronoName)), gronoName);
							gronoNameManager.setNode(gronoName, gronoNodeId);
							logger.info("grono added ["+gronoNodeId+"] - " + gronoName);
						}
						if (! graph.hasEdge(userNodeId, gronoNodeId)) {
							graph.addEdge(userNodeId, gronoNodeId);
							logger.info(" - add edge ["+userNodeId+"]" + id + " - ["+gronoNodeId+"]" + gronoName);
						}						
					}
				}
				
				if (userIndividual.hasPropertyValue(propertyPochodziZ)) {
					String fromName = (String) userIndividual.getPropertyValue(propertyPochodziZ);
					fromName = fromName.trim();
					if (! "".equals(fromName)) {
						Integer fromNodeId = cityManager.getNode(fromName);
						if (fromNodeId == null) {
							NodeData fromNodeData = new NodeData(NodeData.MIASTO, fromName);
							fromNodeId = graph.addNode(NodeData.MIASTO,dataManager.getInt(fromNodeData),fromName);
							cityManager.setNode(fromName, fromNodeId);
							logger.info("city added ["+fromNodeId+"] - " + fromName);
						}
						graph.addEdge(userNodeId, fromNodeId);
						logger.info(" - add edge ["+userNodeId+"]" + id + " - ["+fromNodeId+"]" + fromName);
					}					
				}
			}
			
			logger.info("users parsed");
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		
		//Graph[] result = new Graph[2];
		Graph[] result = new Graph[1];

		/*graph = new Graph();
		int one1 = graph.addNode(dataManager.getInt(new NodeData(NodeData.NodeTypes.imie, "one")), "one");
		int one2 = graph.addNode(dataManager.getInt(new NodeData(NodeData.NodeTypes.imie, "one")), "one");
		int two1 = graph.addNode(dataManager.getInt(new NodeData(NodeData.NodeTypes.imie, "two")), "two");
		graph.addEdge(one1, two1);
		graph.addEdge(one2, two1);
		//graph.addEdge(one1, one2);
		//result[0] = graph;
		
		//graph = new Graph();
		int one3 = graph.addNode(dataManager.getInt(new NodeData(NodeData.NodeTypes.imie, "one")), "one");
		int one4 = graph.addNode(dataManager.getInt(new NodeData(NodeData.NodeTypes.imie, "one")), "one");
		int two2 = graph.addNode(dataManager.getInt(new NodeData(NodeData.NodeTypes.imie, "two")), "two");
		graph.addEdge(one3, one4);
		graph.addEdge(one3, two2);
		//result[1] = graph;*/
		result[0] = graph;
		
		logger.info("Parser ended");
		return result;
	}
	
	private void addNode() {
		
	}
	
}
